Skip to content

fix: allow unquoted names DuckDB reserves but Snowflake doesn't - #482

Open
THEtheChad wants to merge 5 commits into
tekumara:mainfrom
THEtheChad:fix/duckdb-reserved-words
Open

THEtheChad wants to merge 5 commits into
tekumara:mainfrom
THEtheChad:fix/duckdb-reserved-words

Conversation

@THEtheChad

@THEtheChad THEtheChad commented Oct 27, 2026 •

Copy link
Copy Markdown

Fixes #481.

Unquoted table and column names such as semi, pivot and verbose work in Snowflake but failed with a DuckDB parser error. sqlglot's DuckDB generator already quotes known reserved words, but its list is incomplete (reported as tobymao/sqlglot#8804; 30.25.0 added PIVOT, UNPIVOT, LAMBDA and SUMMARIZE, but not the rest).

  • Extend the DuckDB generator's reserved-word set with the reserved and type_function categories from duckdb_keywords(), read once through a short-lived in-memory connection at import. The custom FakesnowDuckDB dialect registers separately and does not modify sqlglot's duckdb dialect.
  • Exclude Snowflake's documented always-reserved categories from these additional words, preserving sqlglot's existing quoting rules. This keeps by, check, create, group, ilike, in, is, like, null, qualify and tablesample rejected as unquoted table and column names. The Snowflake list is maintained manually because sqlglot's Snowflake dialect has no reserved-keyword list to reuse.
  • Use the dialect for cursor statements, COPY INTO's generated INSERTs, and the DESCRIBE query that expands SELECT * in typed CREATE TABLE AS. Explicitly quote column references synthesized from DESCRIBE results and Parquet field names, so names such as "NULL" and "QUALIFY" retain their values in CTAS and COPY transformations.
  • Quote user-written identifiers only during SQL generation, after uppercasing and name resolution. Generated quotes do not change the AST's quoted flags or the case of names supplied in double quotes.

Tests cover the affected words as table and column names, metadata casing, quoted lookup and mixed case, ALTER/DROP, UPDATE/DELETE/MERGE, CTEs and aliases, typed CREATE TABLE AS, COPY INTO, and server mode. Another 22 cases check rejection of the 11 Snowflake-reserved words; four cases preserve quoted reserved column names in CTAS and Parquet COPY transformations.

Some accepted Snowflake syntax still fails earlier in sqlglot's Snowflake parser (reported as tobymao/sqlglot#8805):

  • returning and glob as table names or column references
  • concurrently as a table name
  • select using from t
  • create table t (exclude int)
  • select lateral from t
  • describe table pivot

ALTER TABLE pivot RENAME COLUMN semi TO anti works and is covered by a test.

This does not add Snowflake keyword validation. Context-dependent words remain quoted in all contexts: Snowflake accepts asof, left, right, full and natural as column names and in CREATE TABLE, but requires quotes around these table names in a SELECT's FROM clause. fakesnow also accepts those unquoted SELECTs, consistent with the README's existing caveat about accepting a more liberal dialect. Existing differences for some, minus, increment, regexp and rlike remain outside this change.

Checked against a live Snowflake account (10.38): the #481 examples and new positive test cases, including rename and typed CREATE TABLE AS; error 001003 (42000) for all 22 reserved-name rejection cases; and preserved values for "NULL"/"QUALIFY" in typed CTAS and Parquet COPY transformations (using an internal stage for the live COPY check).

🤖 Generated with Claude Code

THEtheChad and others added 2 commits October 26, 2026 18:14
A table or column named with an unquoted word such as SEMI, PIVOT, LAMBDA
or VERBOSE failed with a DuckDB parser error. Snowflake doesn't reserve
these words, so it creates and reads such tables and columns. DuckDB does
reserve them, and sqlglot's DuckDB generator only quotes the reserved
words in its own list, which misses many (SEMI, ANTI, POSITIONAL,
SIMILAR, OVERLAPS, FREEZE, VERBOSE, ASOF and CONCURRENTLY, even in sqlglot
30.25.0).

Generate DuckDB SQL with a DuckDB dialect that quotes every identifier
DuckDB can't take unquoted, taking the words from DuckDB's own
duckdb_keywords() (the reserved and type_function categories), so the
list matches the installed DuckDB. Statements and COPY INTO inserts are
generated with it. Names are already upper-cased before generation and
DuckDB matches quoted identifiers case-insensitively, so names resolve
as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the DuckDB dialect for the internal SELECT-star description. Split reserved-name operation tests so each result is checked independently, and cover column renaming and preservation of quoted case.

Co-Authored-By: GPT-6 Astra <noreply@openai.com>
THEtheChad added a commit to THEtheChad/fakesnow that referenced this pull request Oct 27, 2026
# Conflicts:
#	fakesnow/transforms/transforms.py
#	tests/test_fakes.py
#	tests/test_server.py
THEtheChad and others added 2 commits October 26, 2026 19:31
Quoting every word DuckDB reserves also quoted words Snowflake reserves in every context. DuckDB's list adds by, check, create, group, ilike, in, is, like, null, qualify and tablesample, and quoting them made fakesnow accept statements such as create table example (qualify int) or create table group (x int). Snowflake rejects these, and fakesnow rejected them before because DuckDB did.

Leave Snowflake's documented reserved keywords (the ones reserved by ANSI or by Snowflake, https://docs.snowflake.com/en/sql-reference/reserved-keywords) out of the extra quoting, so DuckDB rejects them as it did before. Words Snowflake reserves only in some contexts, such as asof, lateral or left, stay quoted, because Snowflake accepts them as column names. sqlglot's Snowflake dialect has no reserved-keyword list to reuse.

Checked against a live Snowflake account (10.38): each of the 11 words is rejected with 001003 (42000) in create table <word> (x int) and create table example (<word> int).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quote schema-derived CTAS columns and Parquet field references so excluding Snowflake-reserved keywords cannot turn NULL into a literal or reject QUALIFY. Add four regression cases and document manual keyword-list maintenance.

Checked against live Snowflake 10.38.102: typed CTAS and Parquet COPY preserve values for NULL and QUALIFY (COPY via an internal stage).

Co-Authored-By: GPT-6 Astra <noreply@openai.com>
THEtheChad added a commit to THEtheChad/fakesnow that referenced this pull request Oct 27, 2026
THEtheChad added a commit to THEtheChad/fakesnow that referenced this pull request Oct 27, 2026
On the fork's main, the reserved-word quoting (tekumara#482) and the follow-ups
to tekumara#471 and tekumara#473 make those cases match Snowflake, so their marks are
gone. The keywords that still differ cite the sqlglot issues behind them
(tobymao/sqlglot#8804, #8805); tekumara#482 also makes fakesnow accept QUALIFY,
ILIKE and TABLESAMPLE as column names in CREATE TABLE, which Snowflake
refuses, so those are marked like UNION and SAMPLE. The LOCALTIMESTAMP
and DATE_TRUNC cases cite tobymao/sqlglot#8807 and #8806.

Newly marked after the live run, each checked against Snowflake: TRIM
and its variants ignore the characters to remove, MERGE doesn't detect a
target row matched twice, the in-process PUT result is described as one
column, and a VARCHAR(n) column's result metadata loses n. A mark that
had landed on ddl.describe-table belongs to ddl.show-schemas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
THEtheChad added a commit to THEtheChad/fakesnow that referenced this pull request Oct 27, 2026
QUALIFY, ILIKE and TABLESAMPLE as unquoted column names are refused
again on fork main, as Snowflake refuses them, so the live run reported
their marks as now matching. Removes the marks and their pinned
fingerprints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
typos read "pIvOt" as a misspelling of "to"; "PiVoT" is still a quoted
mixed-case PIVOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unquoted names that DuckDB reserves but Snowflake doesn't fail (SEMI, PIVOT, RETURNING, ...)

1 participant